package com.sansi.pencilbook.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sansi.pencilbook.DO.Result;
import com.sansi.pencilbook.Enum.BehaviorEnum;
import com.sansi.pencilbook.Enum.RelationEnum;
import com.sansi.pencilbook.entity.*;
import com.sansi.pencilbook.mapper.BookMapper;
import com.sansi.pencilbook.service.*;
import com.sansi.pencilbook.util.ArraysUtil;
import com.sansi.pencilbook.util.ExcelUtil;
import com.sansi.pencilbook.util.ISBNUtil;
import com.sansi.pencilbook.util.RecommendUtil;
import jxl.Sheet;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.*;

/**
 * 功能说明: 书籍的服务
 *
 * @author lsy13
 */
@Slf4j
@Service
public class BookServerImpl implements BookServer {

    @Autowired
    private BookMapper bookMapper;

    @Autowired
    private UserBookServer userBookServer;

    @Autowired
    private UserBookRelationServer userBookRelationServer;

    @Autowired
    private UserServer userServer;

    @Autowired
    private CategoryServer categoryServer;

    @Override
    public Result getBooksAll() {
        try {
            List<Book> books = bookMapper.findAll();
            return books.size()>0 ? Result.ofSuccess("查询成功", books.size(), books) :
                    Result.ofFailed("目前不存在书籍");
        } catch (SQLException throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    @Override
    public Result getBooksAll(int pageNum, int pageSize) {
        Page<Book> page = PageHelper.startPage(pageNum, pageSize);
        try {
            bookMapper.findAll();
            return page.getTotal()>0 ? Result.ofSuccess("查询成功", page.size(), page.getResult()) :
                    Result.ofFailed("查询结果为空");
        } catch (Exception throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    /**
     * 获取推荐书籍列表
     * @param userId 传入用户Id
     * @return 返回推荐书籍列表
     */
    @Override
    public Result getRecommendBooks(int userId) {
        //去重
        LinkedHashSet<Book> recommendBooks = new LinkedHashSet<>();
        try {
            log.info("=======开始获取推荐列表=======");
            List<Book> books = bookMapper.findAll();
            List<User> users = (List<User>) userServer.getAllUsers().getData();
            double[][] score = new double[1][books.size()];
//            String[] booksId = new String[books.size()];
            double[][] otherScore = new double[users.size()-1][books.size()];
//            String[] usersId = new String[users.size()];
            for (int i=0;i<books.size();i++) {
//                booksId[i] = books.get(i).getBookIsbn();
                score[0][i] = getScore(userId, books.get(i));
            }
            boolean flag = false;
            for (int i=0;i<users.size();i++) {
//                usersId[i] = String.valueOf(users.get(i).getUserId());
                if (userId == users.get(i).getUserId()) {
                    flag = true;
                    continue;
                }
                for (int j=0;j<books.size();j++) {
                    otherScore[flag ? i-1 : i][j] = getScore(users.get(i).getUserId(), books.get(j));
                }
            }
            //将所有用户数据合并
            double[][] allScore = ArraysUtil.addAll(otherScore, score);
            //获取相似度
            double[] similarity = RecommendUtil.getSimilarities(allScore, allScore.length);
            log.info("相似度: " + Arrays.toString(similarity));
            //获取相似度最大的前K个用户
            int[] maxSimUserIndex = ArraysUtil.getMaxArrayIndex(5, similarity);
            log.info("相似度最大的前K位: " + Arrays.toString(maxSimUserIndex));
            double avgUserScore = RecommendUtil.getAvgScore(score[0]);
            //存放感兴趣书籍的分数
            List<Double> allInterBookScore = new ArrayList<>();
            //存放感兴趣书籍的下标
            List<Integer> allInterBookIndex = new ArrayList<>();
            for (int simUserIndex : maxSimUserIndex) {
                int[] maxIntBookIndex = ArraysUtil.getMaxArrayIndex(5, otherScore[simUserIndex]);
                for (int intBookIndex : maxIntBookIndex) {
                    //对书籍的感兴趣程度
                    double numerator = 0.0;
                    double denominator = 0.0;
                    for (int userIndex : maxSimUserIndex) {
                        //用户对于书籍存在评分
                        if (otherScore[userIndex][intBookIndex] != 0) {
                            double avgOtherUserScore = RecommendUtil.getAvgScore(otherScore[userIndex],
                                otherScore[userIndex][intBookIndex]);
                            numerator = numerator + (otherScore[userIndex][intBookIndex]-avgOtherUserScore) * similarity[userIndex];
                            denominator = denominator + Math.abs(similarity[userIndex]);
                        }
                    }
                    double interScore = Math.min((avgUserScore + numerator / denominator), 5);
                    allInterBookScore.add(interScore);
                    allInterBookIndex.add(intBookIndex);
                }
            }
            log.info("推荐的书籍预测分数:" + allInterBookScore.toString());
            int[] ind = ArraysUtil.getSortIndex(ArraysUtil.unwrap(allInterBookScore.toArray(new Double[0])));
            for (int k : ind) {
                recommendBooks.add(books.get(allInterBookIndex.get(k)));
                if (recommendBooks.size() == 9) {
                    break;
                }
            }
            while (recommendBooks.size()<9) {
                log.info("=============进入随机推荐=============");
                int i = (int) (Math.random() * books.size());
                recommendBooks.add(books.get(i));
            }
            log.info("推荐的书籍:" + recommendBooks.toString());
            log.info("========获取推荐列表结束==========");
            return recommendBooks.size()>0 ? Result.ofSuccess("获取推荐列表成功", recommendBooks.size(), recommendBooks) :
                Result.ofFailed("没有推荐列表");
        } catch (Exception throwables) {
            throwables.printStackTrace();
            return Result.ofFailed(throwables.getMessage());
        }
    }

    /**
     * 获取用户对某本书的隐性分数
     * @param userId
     * @param book
     * @return
     */
    public double getScore(int userId, Book book) {
        List<UserBook> browse = (List<UserBook>) userBookServer.getUserBookByUserBookBehavior(userId, book.getBookIsbn(), BehaviorEnum.BROWSE).getData();
        List<UserBook> purchase = (List<UserBook>) userBookServer.getUserBookByUserBookBehavior(userId, book.getBookIsbn(), BehaviorEnum.PURCHASE).getData();
        List<UserBook> likelist = (List<UserBook>) userBookServer.getUserBookByUserBookBehavior(userId, book.getBookIsbn(), BehaviorEnum.LIKE).getData();
        List<UserBook> blacklist = (List<UserBook>) userBookServer.getUserBookByUserBookBehavior(userId, book.getBookIsbn(), BehaviorEnum.BLACKLIST).getData();
        Result collect = userBookRelationServer.getUserBookRelationByUserBookRelation(userId, book.getBookIsbn(), RelationEnum.COLLECT);
        Result black = userBookRelationServer.getUserBookRelationByUserBookRelation(userId, book.getBookIsbn(), RelationEnum.BLACK);
        Result cart = userBookRelationServer.getUserBookRelationByUserBookRelation(userId, book.getBookIsbn(), RelationEnum.CART);
        Result like = userBookRelationServer.getUserBookRelationByUserBookRelation(userId, book.getBookIsbn(), RelationEnum.LIKE);
        Result unlike = userBookRelationServer.getUserBookRelationByUserBookRelation(userId, book.getBookIsbn(), RelationEnum.UNLIKE);
        double score = 0.0;
        if (browse==null && purchase==null && collect.isSuccess() && cart.isSuccess() && black.isSuccess()) {
            return score;
        }
        if (browse!=null) {
            score += (Math.min(browse.size(), 3))*0.5;
        }
        if (purchase!=null) {
            score += (Math.min(purchase.size(), 2))*1.5;
        }
        if (collect.getEffectRow()>0) {
            score += 1.5;
        }
        if (cart.getEffectRow()>0) {
            score += 1.0;
        }
        if (black.getEffectRow()>0) {
            score -= 2.0;
        }
        /*score = score + browse.size()>10 ? 1.0 : browse.size()*0.1
            + collect.getEffectRow()>0 ? 1.0 : 0.0
            + cart.getEffectRow()>0 ? 1.0 : 0.0
            + purchase.size()>=2 ? 2.0 : 1.5
            + black.getEffectRow()>0 ? -2.0 : 0.0;*/
//todo: 后续加入时间影响
//        if (black.getEffectRow()>0 && like.getEffectRow()>0) {
//            return score>=3.0 ? score : score/2.0;
//        }else if (black.getEffectRow()>0) {
//            return 0.0;
//        }else if (like.getEffectRow()>0) {
//            return 5.0;
//        }
        return Math.min(score, 5.0);
    }

    @Override
    public Result getBooksByCategory(int categoryId) {
        List<Book> list = null;
        try {
            List<Category> categories = (List<Category>) categoryServer.getCategoriesByParent(categoryId).getData();
            list = bookMapper.findByCategoryId(categoryId);
            if (categories != null || categories.size()>0) {
                for (Category category : categories) {
                    list.addAll(bookMapper.findByCategoryId(category.getCategoryId()));
                }
            }
            return list.size()>0 ? Result.ofSuccess("查询成功", list.size(), list) :
                    Result.ofFailed("该类别目前不存在书籍");
        } catch (SQLException throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    @Override
    public Result getBooksByName(String name) {
        try {
            List<Book> books = bookMapper.findByName(name);
            return books.size()>0 ? Result.ofSuccess("查询成功", books.size(), books) :
                    Result.ofFailed("书籍不存在");
        } catch (SQLException throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    @Override
    public Result getBooksByName(String name, int pageNum, int pageSize) {
        Page<Book> page = PageHelper.startPage(pageNum, pageSize);
        try {
            bookMapper.findByName(name);
            return page.getTotal()>0 ? Result.ofSuccess("查询成功", page.size(), page.getResult()) :
                    Result.ofFailed("查询结果为空");
        } catch (Exception throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    @Override
    public Result getBookByIsbn(String isbn) {
        try {
            Book book = bookMapper.findByISBN(isbn);
            return book!=null ? Result.ofSuccess("查询成功", 1, book) :
                    Result.ofFailed("书籍不存在");
        } catch (SQLException throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    @Override
    public Result getBooksByAuthor(String author) {
        try {
            List<Book> books = bookMapper.findByAuthor(author);
            return books.size()>0 ? Result.ofSuccess("查询成功", books.size(), books) :
                    Result.ofFailed("查询不到该作者的相关书籍");
        } catch (Exception throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    @Override
    public Result getBooksByAuthor(String author, int pageNum, int pageSize) {
        Page<Book> page = PageHelper.startPage(pageNum, pageSize);
        try {
            bookMapper.findByAuthor(author);
            return page.getTotal()>0 ? Result.ofSuccess("查询成功", page.size(), page.getResult()) :
                    Result.ofFailed("查询结果为空");
        } catch (Exception throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    @Override
    public Result getBooksByPublisher(String publisher) {
        try {
            List<Book> books = bookMapper.findByPublisher(publisher);
            return books.size()>0 ? Result.ofSuccess("查询成功", books.size(), books) :
                    Result.ofFailed("查询不到该出版社的书籍");
        } catch (Exception throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    @Override
    public Result getBooksByPublisher(String publisher, int pageNum, int pageSize) {
        Page<Book> page = PageHelper.startPage(pageNum, pageSize);
        try {
            bookMapper.findByPublisher(publisher);
            return page.getTotal()>0 ? Result.ofSuccess("查询成功", page.size(), page.getResult()) :
                    Result.ofFailed("查询结果为空");
        } catch (Exception throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    @Override
    public Result getBookByRelation(int userId, RelationEnum relationEnum) {
        List<UserBookRelation> list = (List<UserBookRelation>) userBookRelationServer.getUserBookRelationByUserRelation(userId, relationEnum).getData();
        List<Book> books = new ArrayList<>();
        try{
            for (UserBookRelation userBookRelation : list) {
                books.add(bookMapper.findByISBN(userBookRelation.getBookIsbn()));
            }
            return books.size()>0 ? Result.ofSuccess("查询成功", books.size(), books) :
                Result.ofFailed("查询为空");
        }catch (Exception e) {
            System.out.println(e.getMessage());
            return Result.ofFailed(e.getMessage());
        }
    }

    @Override
    public Result getBookByRelation(int userId, RelationEnum relationEnum, int pageNum, int pageSize) {
        List<UserBookRelation> list = (List<UserBookRelation>) userBookRelationServer.getUserBookRelationByUserRelation(userId, relationEnum, pageNum, pageSize).getData();
        List<Book> books = new ArrayList<>();
        try{
            for (UserBookRelation userBookRelation : list) {
                books.add(bookMapper.findByISBN(userBookRelation.getBookIsbn()));
            }
            return books.size()>0 ? Result.ofSuccess("查询成功", books.size(), books) :
                Result.ofFailed("查询为空");
        }catch (Exception e) {
            System.out.println(e.getMessage());
            return Result.ofFailed(e.getMessage());
        }
    }

    @Override
    public Result getBooksByTime() {
        try {
            List<Book> books = bookMapper.findByTime();
            return books.size()>0 ? Result.ofSuccess("查询成功", books.size(), books) :
                Result.ofFailed("查询不到新书版的书籍");
        } catch (Exception throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    @Override
    public Result getBooksByTime(int pageNum, int pageSize) {
        Page<Book> page = PageHelper.startPage(pageNum, pageSize);
        try {
            bookMapper.findByTime();
            return page.getTotal()>0 ? Result.ofSuccess("查询成功", page.size(), page.getResult()) :
                Result.ofFailed("查询结果为空");
        } catch (Exception throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    @Override
    public Result getBooksByCategory(int categoryId, int pageNum, int pageSize) {
        Page<Book> page = PageHelper.startPage(pageNum, pageSize);
        try {
            bookMapper.findByCategoryId(categoryId);
            return page.getTotal()>0 ? Result.ofSuccess("查询成功", page.size(), page.getResult()) :
                    Result.ofFailed("查询结果为空");
        } catch (Exception throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("查询失败");
        }
    }

    @Override
    public Result addBook(Book book) {
        try {
            return Result.ofSuccess("添加书籍成功", bookMapper.saveBook(book));
        } catch (Exception throwables) {
            Throwable cause = throwables.getCause();
            if (cause instanceof SQLIntegrityConstraintViolationException) {
                String errmsg = cause.getMessage();
                //根据约束名称定位是那个字段
                if(StringUtils.isNotBlank(errmsg)) {
                    return Result.ofFailed("该书籍已经添加");
                }
            }
            //处理非唯一约束
            return Result.ofFailed("添加书籍失败");
        }
    }

    @Override
    public Result addBooks(List<Book> books) {
        try {
            return Result.ofSuccess("批量添加书籍成功", bookMapper.saveBooks(books));
        } catch (Exception throwables) {
            Throwable cause = throwables.getCause();
            if (cause instanceof SQLIntegrityConstraintViolationException) {
                String errmsg = cause.getMessage();
                //根据约束名称定位是那个字段
                if(StringUtils.isNotBlank(errmsg)) {
                    return Result.ofFailed("存在已添加书籍");
                }
            }
            //处理非唯一约束
            return Result.ofFailed("批量添加书籍失败");
        }
    }

    @Override
    public Result addBooksByExcel(File xlsFile) {
        try {
            Sheet sheet = ExcelUtil.getSheet(xlsFile, 0);
            if (sheet.getColumns() != 9) {
                return Result.ofFailed("表格上传错误");
            }
            List<Book> books = new ArrayList<>();
            for (int row=1;row<sheet.getRows();row++) {
                Book book = new Book();
                for (int col=0;col<sheet.getColumns();col++) {
                    switch (sheet.getCell(col,0).getContents()) {
                        case "ISBN" :
                            String isbn = StringUtils.trim(sheet.getCell(col,row).getContents());
                            if ("".equals(isbn)) {
                                continue;
                            }
                            if (ISBNUtil.checkISBN(isbn)) {
                                for (Book tmp : books) {
                                    if (tmp.getBookIsbn().equals(isbn)) {
                                        return Result.ofFailed("ISBN为:" + isbn + "的书籍重复添加");
                                    }
                                }
                                book.setBookIsbn(isbn);
                            }else {
                                return Result.ofFailed((row+1) + "行的ISBN不正确");
                            }
                            break;
                        case "类别" :
                            Category category = (Category) (categoryServer.getCategoryByName(StringUtils.trim(sheet.getCell(col,row).getContents())).getData());
                            if (category != null) {
                                book.setCategoryId(category.getCategoryId());
                            }else {
                                return Result.ofFailed("ISBN为:" + book.getBookIsbn() + "的书籍类别不存在");
                            }
                            break;
                        case "图片" :
                            book.setBookImg(StringUtils.trim(sheet.getCell(col,row).getContents()));
                            break;
                        case "书名" :
                            book.setBookName(StringUtils.trim(sheet.getCell(col,row).getContents()));
                            break;
                        case "出版社" :
                            book.setBookPublishers(StringUtils.trim(sheet.getCell(col,row).getContents()));
                            break;
                        case "价格" :
                            book.setBookPrice(Float.parseFloat(StringUtils.trim(sheet.getCell(col,row).getContents())));
                            break;
                        case "作者" :
                            book.setBookAuthor(StringUtils.trim(sheet.getCell(col,row).getContents()));
                            break;
                        case "简介" :
                            book.setBookIntroduction(StringUtils.trim(sheet.getCell(col,row).getContents()));
                            break;
                        case "出版日期" :
                            book.setBookTime(StringUtils.trim(sheet.getCell(col,row).getContents()));
                            break;
                    }
                }
                books.add(book);
            }
            return addBooks(books);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return Result.ofFailed(e.getMessage());
        }
    }

    @Override
    public Result modifyBook(Book book) {
        try {
            return Result.ofSuccess("修改书籍成功", bookMapper.updateBook(book));
        } catch (SQLException throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("修改书籍失败");
        }
    }

    @Override
    public Result modifyBooks(List<Book> books) {
        try {
            int num = bookMapper.updateBooks(books);
            return num>0 ? Result.ofSuccess("批量修改书籍成功", num) :
                Result.ofFailed("批量修改书籍失败");
        } catch (Exception throwables) {
            System.out.println(throwables.getMessage());
            return Result.ofFailed("修改书籍失败");
        }
    }

}
